package com.inspur.security.cbb3.kms.utils;

import com.inspur.security.cbb3.kms.enums.*;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.encoders.Hex;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 正则校验工具-封装项目用到的所有校验
 * @author lijunchang
 */
public class RegExpUtil {

    /**
     * 用户名称正则，英文、数字、下划线。5-20位
     */
    public static final String userNameReg = "^[a-zA-Z0-9_-]{4,16}$";

    /**
     * 用户密码正则，至少一个小写字母、一个大写字母、一个数字和一个特殊字符。8-20位
     */
    public static final String userPwdReg = "^(?![A-Za-z0-9]+$)(?![a-z0-9\\W]+$)(?![A-Za-z\\W]+$)(?![A-Z0-9\\W]+$)[a-zA-Z0-9\\W]{8,20}$";

    /**
     * 邮箱校验
     */
    public static final String emailReg = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";

    /**
     * 随机名称校验
     */
    public static final String randomReg="^[A-Za-z0-9]{32}$";

    /**
     * 密钥名称正则：不以数字开头，不包含<>，1到50位
     */
    public static final String keyNameReg="^[^0-9^<^>][^<^>]{0,49}$";

    /**
     * 密钥邮箱正则
     */
    public static final String keyEmailReg="^([A-Za-z0-9_\\-\\.])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,4})$";

    /**
     * 密钥密码正则，至少一个小写字母、一个大写字母、一个数字和一个特殊字符。8-20位
     */
    public static final String keyPwdReg="^(?![A-Za-z0-9]+$)(?![a-z0-9\\W]+$)(?![A-Za-z\\W]+$)(?![A-Z0-9\\W]+$)[a-zA-Z0-9\\W]{8,20}$";

    /**
     * 密钥加密算法正则，目前只是有一种算法RSA，不区分大小写
     */
    public static final String keyTypeReg="^[Rr][Ss][Aa]$";

    /**
	 * 有效期格式校验：yyyy-MM-dd格式
	 */
	public static final String keyExpiryReg = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)$";

    public static final String enumReg="^[0-9]$";
    public static boolean isUserName(String userName) {
        if (StringUtils.isBlank(userName)) {
            return false;
        }
        return userName.matches(userNameReg);
    }

    public static boolean isUserPassword(String userPassword) {
        if (StringUtils.isBlank(userPassword)) {
            return false;
        }
        return userPassword.matches(userPwdReg);
    }

    public static boolean isEmail(String email) {
        if (StringUtils.isBlank(email)) {
            return false;
        }
        return email.matches(emailReg);
    }

    public static boolean checkKeyName(String keyName) {
		if (StringUtils.isBlank(keyName)) {
			return false;
		}
		return keyName.matches(keyNameReg);
	}

    public static boolean checkKeyEmail(String keyEmail) {
		if (StringUtils.isBlank(keyEmail)) {
			return false;
		}
		return keyEmail.matches(keyEmailReg);
	}

    public static boolean checkKeyPwd(String keyPwd) {
		if (StringUtils.isBlank(keyPwd)) {
			return false;
		}
		return keyPwd.matches(keyPwdReg);
	}

    public static boolean checkKeyType(String keyType) {
		if (StringUtils.isBlank(keyType)) {
			return false;
		}
		return keyType.matches(keyTypeReg);
	}

    /**
     * 校验密钥有效期，可选
     * @param keyExpiry
     * @return
     */
    public static boolean checkKeyExpiry(String keyExpiry) {
		if (StringUtils.isBlank(keyExpiry)) {
			return true;
		}
		String nowTime = DateUtil.getStringDateShort();
		if (keyExpiry.compareTo(nowTime)<=0) {
			return false;
		}
		return keyExpiry.matches(keyExpiryReg);
	}


    /**
     * 时间格式校验：yyyy-MM-dd HH:mm:ss
     * @param t 时间
     * @return 校验结果
     */
    public static boolean checkTimeFormat(String t){
        if(StringUtils.isBlank(t)){
            return true;
        }
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        boolean flag = true;
        try {
            //Java 8 新添API 用于解析日期和时间
            LocalDateTime.parse(t, dtf);
        } catch (Exception e) {
            flag = false;
        }
        return flag;
    }

    /**
     * 数据操作类型校验
     * @param action 数据操作类型，Hash、encrypt、decrypt、sign、verify
     * @return 校验结果
     */
    public static boolean checkDataAction(String action){
        if (DataActionType.getByType(action)==DataActionType.other){
            return false;
        }
        return true;
    }

    /**
     * 算法校验 rsa、dsa、sm2、des、aes、sm4
     * @param alg 算法名称
     * @return 校验结果
     */
    public static boolean checkAlgorithm(String alg){
        if (AlgorithmType.getByType(alg)==null){
            return false;
        }
        return true;
    }

    /**
     * 校验数据操作类型和算法是否匹配
     * @param action
     * @param alg
     * @return
     */
    public static boolean checkDataActionAndAlgorithm(String action, String alg){
        //校验数据操作类型是否合法
        if (DataActionType.getByType(action)==null){
            return false;
        }
        //校验算法类型是否合法
        if (AlgorithmType.getByType(alg)==null){
            return false;
        }
        //校验action与alg是否匹配
        switch (DataActionType.getByType(action)){
            case hash:
                switch (AlgorithmType.getByType(alg)){
                    case sm3:
                    case md5:
                    case sha1:
                    case sha256:
                        return true;
                    default:
                        return false;
                }
            case encrypt:
            case decrypt:
                switch (AlgorithmType.getByType(alg)){
                    case aes:
                    case desede:
                    case sm4:
                    case rsa:
                    case sm2:
                        return true;
                    default:
                        return false;
                }
            case sign:
            case verify:
                switch (AlgorithmType.getByType(alg)){
                    case rsa:
                    case sm2:
                    case dsa:
                        return true;
                    default:
                        return false;
                }
            default:
                return false;
        }
    }

    /**
     * 校验待计算数据长度，小于10MB
     * @return 校验结果
     */
    public static boolean checkData(String data,int alowLength){
        if (StringUtils.isBlank(data)){
            return false;
        }
        byte[] buff = data.getBytes();
        int dataBytesLength = buff.length;
        if (dataBytesLength > alowLength){
            return false;
        }
        return true;
    }

    /**
     * 校验待计算数据长度，小于10MB
     * @return 校验结果
     */
    public static boolean checkRsaData(String data){
        if (StringUtils.isBlank(data)){
            return false;
        }
        byte[] buff = data.getBytes();
        int dataBytesLength = buff.length;
        if (dataBytesLength > RsaDataLength.rsa1024.getBitLength()){
            return false;
        }
        return true;
    }

    /**
     *  校验字符串是否是base64编码
     * @param str
     * @return 校验结果
     */
    public static boolean checkBase64(String str) {
        String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$";
        return Pattern.matches(base64Pattern, str);
    }

    /**
     *  校验字符串是否是hex编码
     * @param str
     * @return 校验结果
     */
    public static boolean checkHex(String str) {
        String hexPattern = "^[0-9a-fA-F]+$";
        return Pattern.matches(hexPattern, str);
    }

    /**
     * 校验cbc加密模式IV
     * @param alg 算法
     * @param iv 偏移量
     * @return
     */
    public static boolean checkIv(String alg, String iv) {
        if (!checkHex(iv)){
            return false;
        }
        if (StringUtils.isNotEmpty(iv)){
            int ivLength = iv.length();
            return ArrayUtils.contains(IvLength.getByType(alg).getBitLength(),ivLength);
        }else {
            return false;
        }
    }

    /**
     * 校验数据编码格式
     * @param encodeMethod 编码名称
     * @return 校验结果
     */
    public static boolean checkDataContentEncoding(String encodeMethod){

        if (DataEncodingType.getByType(encodeMethod)==null){
            return false;
        }
        return true;
    }

    /**
     * 校验二级密钥Kek的算法，aes、sm4
     * @param alg 算法名称
     * @return
     */
    public static boolean checkKekAlgorithm(String alg){
        if(AlgorithmType.aes.name().equalsIgnoreCase(alg)|| AlgorithmType.sm4.name().equalsIgnoreCase(alg)){
            return true;
        }
        return false;
    }

    /**
     * 判断是否时对称密钥
     * @param alg
     * @return true是，false否
     */
    public static boolean isSymmetricAlgorithm(String alg){
        switch (AlgorithmType.getByType(alg)){
            case sm4:
            case desede:
            case aes:
                return true;
            default:
                return false;
        }
    }

    /**
     * 校验加密模式
     * @param encryptMode 加密模式
     * @return true符合，false否
     */
    public static boolean checkEncryptMode(String encryptMode) {
        if(EncryptMode.getByType(encryptMode)==null){
            return false;
        }
        return true;
    }

    /**
     * 校验加密填充模式
     * @param paddingMode 填充模式
     * @return true符合，false否
     */
    public static boolean checkPaddingMode(String paddingMode) {
        if (PaddingMode.getByType(paddingMode)==null){
            return false;
        }
        return true;
    }

    /**
     * 校验字符串和编码格式是否匹配，base64/hex
     * @param alg 算法
     * @param str 待校验字符串
     * @param encodingType 编码格式
     * @return
     */
    public static boolean isStrMatchEncoding(String alg,String str,String encodingType){
        switch (DataOutputType.getByType(encodingType)){
            case Hex:
                //使用hex解码器解码
                try {
                    byte[] decode = Hex.decode(str);
                    if (ArrayUtils.isNotEmpty(decode)){
                        //校验长度
                        return Arrays.asList(AlgorithmType.getByType(alg).getBitLength()).contains(String.valueOf(decode.length*8));
                    }
                    return false;
                }catch (Exception e){
                    return false;
                }
            case Base64:
                //使用base64解码器解码
                try{
                    byte[] decode = Base64.getDecoder().decode(str);
                    if (ArrayUtils.isNotEmpty(decode)){
                        //校验长度
                        return Arrays.asList(AlgorithmType.getByType(alg).getBitLength()).contains(String.valueOf(decode.length*8));
                    }
                    return false;
                }catch (Exception e){
                    return false;
                }
            default:
                return false;
        }
    }

    /**
     * 校验密文输出格式
     * @param outputEncoding 输出格式
     * @return
     */
    public static boolean checkDataOutputEncoding(String outputEncoding) {
        if (DataOutputType.getByType(outputEncoding)==DataOutputType.Other){
            return false;
        }
        return true;
    }

    /**
     * 校验对称密钥格式，base64、hex
     * @param symmetricKeyEncoding 对称密钥格式
     * @return
     */
    public static boolean checkSymmetricKeyEncoding(String symmetricKeyEncoding) {
        if (SymmetricKeyFormatType.getByType(symmetricKeyEncoding)==SymmetricKeyFormatType.Other){
            return false;
        }
        return true;
    }

    /**
     * 根据加密类型、方法、长度进行参数校验
     * @param type  类型（对称，非对称）
     * @param algorithm 加密方法，rsa、des等
     * @param bitLength 密钥长度，1024等
     * @return  是否合规，true，合规；false，不合规
     */
    public static boolean checkSecretOrderAlgorithm(String type,String algorithm,String bitLength){
        if(StringUtils.isBlank(type)||StringUtils.isBlank(algorithm)||StringUtils.isBlank(bitLength)){
            return false;
        }
        //校验算法类型是否合法
        switch (SecretKeyType.getByType(type)){
            case Symmetric:
                switch (AlgorithmType.getByType(algorithm)){
                    case aes:
                    case desede:
                    case sm4:
                        return ArrayUtils.contains(AlgorithmType.getByType(algorithm).getBitLength(),bitLength);
                    default:
                        return false;
                }
            case Asymmetric:
                switch (AlgorithmType.getByType(algorithm)){
                    case rsa:
                    case dsa:
                    case sm2:
                        return ArrayUtils.contains(AlgorithmType.getByType(algorithm).getBitLength(),bitLength);
                    default:
                        return false;
                }
            default:
                return false;
        }
    }

    /**
     * 校验签名算法与摘要算法是否匹配
     * @param asymmetricAlgorithm 非对称算法
     * @param hashAlgorithm 摘要算法
     */
    public static boolean checkSignHash(String asymmetricAlgorithm, String hashAlgorithm) {
        switch (AlgorithmType.getByType(asymmetricAlgorithm)){
            case rsa:
                return (RSASignHashAlgorithmType.getByType(hashAlgorithm)!=RSASignHashAlgorithmType.OTHER);
            case dsa:
                return (DSASignHashAlgorithmType.getByType(hashAlgorithm)!=DSASignHashAlgorithmType.OTHER);
            case sm2:
                return (SM2SignHashAlgorithmType.getByType(hashAlgorithm)!=SM2SignHashAlgorithmType.OTHER);
            default:
                return false;
        }
    }
}
